﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Draco.OWF.DataBinding;
using Draco.OWF.Logs;
using Draco.OWF.Exceptions;

namespace Draco.OWF.RT.Engine
{
    public class WorkerEngine : IWorkerEngine
    {
        protected static ILogs m_logger = new Logger();

        protected bool m_Running;
        protected IProcess m_Workflow;
        protected IBindingDataItemCollection m_Arguments;
        protected IJob m_ProcessInstance;
        protected IJobContext m_RTContext = null;

        protected List<ITask> m_ActivityInstancesRunable = new List<ITask>();
        protected List<ITask> m_ActivityInstancesFired = new List<ITask>();

        #region constructor
        /// <summary>
        /// constructor 
        /// </summary>
        /// <param name="workflow"></param>
        /// <param name="arguments"></param>
        public WorkerEngine(IProcess workflow,IBindingDataItemCollection arguments)
        {
            if (workflow == null)
                throw new ArgumentNullException("process is null");
            m_Workflow = workflow;
            m_Arguments = arguments;
            CreateWorkflowInstance();
        }
        /// <summary>
        /// constructor 
        /// </summary>
        /// <param name="processInstance"></param>
        public WorkerEngine(IJob processInstance)
        {
            if (processInstance == null)
                throw new ArgumentNullException("processInstance is null");
            this.m_ProcessInstance = processInstance;
            this.m_Workflow = processInstance.WorkflowDefine;
            //this.m_RTContext = CreateRTContext();
        }
        #endregion

        /// <summary>
        /// 
        /// </summary>
        public IJob Job
        {
            get { return m_ProcessInstance; }
        }

        #region public method
        /// <summary>
        /// Run process
        /// </summary>
        public virtual void Start(Object startParameters)
        {
            m_logger.Info("start to run Process instance:" + this.m_ProcessInstance.Id);
            if (this.m_ProcessInstance.Status == WFProcessStatus.Completed)
            {
                m_logger.Info("Process instance had stopped");
                return;
            }
            
            m_Running = true;
            while (m_Running)
            {
                List<IEngineCommand> NextCommands = this.GetNextCommands();
                if (NextCommands != null && NextCommands.Count>0)
                {
                    foreach (var cmd in NextCommands)
                        DispatchCommand(cmd);
                }
                else
                {
                    m_Running =false;
                }
            }
        }
        /// <summary>
        /// Stop process
        /// </summary>
        public virtual void Stop()
        {
            this.m_Running = false;
        }
        #endregion

        /// <summary>
        /// Instance Workflow
        /// </summary>
        protected virtual void CreateWorkflowInstance()
        {
            this.m_ProcessInstance = m_Workflow.CreateProcessInstance(m_Arguments);
            this.m_ProcessInstance.Initialize();
        }
        protected virtual void DispatchCommand(IEngineCommand command)
        {
            if (command == null)
                throw new ArgumentNullException("command");
            if(String.IsNullOrEmpty(command.Command) || command.Context == null)
                throw new ArgumentException("Bad command");
            
            switch (command.Command)
            {
                case Instruction.Start:
                    StartProcess(command.Context);
                    break;
                case Instruction.ExecuteActivity:
                    ExecuteActivity(command.AcInstance,command.Context);
                    break;
                case Instruction.MoveToNext:
                    MoveNext(command.AcInstance, command.Context);
                    break;
                case Instruction.Stop:
                    StopProcess(command.Context);
                    break;
                default: break;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected List<IEngineCommand> GetNextCommands()
        {
            if (this.m_ProcessInstance.Status == WFProcessStatus.Inactive)
            {
                this.m_RTContext = CreateRTContext(this.m_ProcessInstance,null);
                this.InitRTContext();
                List<IEngineCommand> list = new List<IEngineCommand>();
                list.Add(this.CreateEngineCommand(Instruction.Start,null, this.m_RTContext));
                return list;
            }
            else if (this.m_ProcessInstance.Status == WFProcessStatus.Running)
            {
                return GetNextRunableCommands();
            }
            else if (this.m_ProcessInstance.Status == WFProcessStatus.Completed)
            {
                return null;
            }
            return null;
        }
        protected virtual List<IEngineCommand> GetNextRunableCommands()
        {
            //
            List<IEngineCommand> cmds = new List<IEngineCommand>();
            if (this.m_ActivityInstancesFired.Count > 0)
            {
                foreach (var Ins in m_ActivityInstancesFired)
                {
                    IEngineCommand cmd = this.CreateEngineCommand(Instruction.MoveToNext, Ins, this.m_RTContext);
                    cmds.Add(cmd);
                }
            }
            if (this.m_ActivityInstancesRunable.Count > 0)
            {
                foreach (var Ins in m_ActivityInstancesRunable)
                {
                    IEngineCommand cmd = this.CreateEngineCommand(Instruction.ExecuteActivity, Ins, this.m_RTContext);
                    cmds.Add(cmd);
                }
            }
            if (cmds.Count == 0 && m_ProcessInstance.Status == WFProcessStatus.Running)
            {
                IEngineCommand cmd = this.CreateEngineCommand(Instruction.Stop, null, this.m_RTContext);
                cmds.Add(cmd);
            }
            return cmds;
        }
        /// <summary>
        /// user can override this method in subclass
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        protected virtual IEngineCommand CreateEngineCommand(String cmd,ITask AcIns, IJobContext context)
        {
            return new EngineCommand() { Command = cmd,AcInstance =AcIns,  Context = context };
        }
        protected virtual IJobContext CreateRTContext(IJob processInstance,IJobContext parent)
        {
            return new JobContext(processInstance, parent);
        }
        protected virtual void InitRTContext()
        {

        }
        #region Run Actions
        protected virtual void StartProcess(IJobContext context)
        {
            IActivity startAc = m_Workflow.StartActivity;
            if (startAc == null)
            {
                throw new WorkflowException("can't find flow entry");
            }
            ITask AcIns = startAc.CreateActivityInstance();
            AcIns.Initialize();
            m_ProcessInstance.AddActivityInstance(AcIns);
            m_ProcessInstance.Status = WFProcessStatus.Running;
            this.m_ActivityInstancesRunable.Add(AcIns);
        }
        protected virtual void ExecuteActivity(ITask AcInstance, IJobContext context)
        {
            AcInstance.Excute(context);
            if (AcInstance.Status == ActivityStatus.Completed)
            {
                this.m_ActivityInstancesRunable.Remove(AcInstance);
                this.m_ActivityInstancesFired.Add(AcInstance);
            }
        }
        protected virtual void MoveNext(ITask AcInstance, IJobContext context)
        {
            IActivity Ac = AcInstance.Activity;
            List<IRoute> Routes = m_Workflow.GetFromActivityRoutes(Ac.Id);
            if (Routes == null || Routes.Count==0)
            {
                this.m_ActivityInstancesFired.Remove(AcInstance);
                return;
            }
            //judg rules
            Routes = JudgRules(Routes, context);
            //MoveNext
            foreach (var IR in Routes)
            {
                IActivity nextAc = m_Workflow.GetActivity(IR.ToId);
                ITask nextAcIns = nextAc.CreateActivityInstance();
                nextAcIns.Initialize();
                this.m_ActivityInstancesFired.Remove(AcInstance);
                this.m_ActivityInstancesRunable.Add(nextAcIns);
            }
        }
        protected virtual List<IRoute> JudgRules(List<IRoute> Routes, IJobContext context)
        {
            return Routes;
        }
        protected virtual void StopProcess(IJobContext context)
        {
            this.m_ProcessInstance.Status = WFProcessStatus.Completed;
        }
        #endregion


        
    }
}
